<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> 
    <title> - 天地维杰网</title>
    <meta name="keywords" content="系统架构,shutdown,不与天斗,Domino,博客,程序员,架构师,笔记,技术,分享,java,Redis">
    
    <meta property="og:title" content="">
    <meta property="og:site_name" content="天地维杰网">
    <meta property="og:image" content="/img/author.jpg"> 
    <meta name="title" content=" - 天地维杰网" />
    <meta name="description" content="天地维杰网 | 博客 | 软件 | 架构 | Java "> 
    <link rel="shortcut icon" href="http://www.shutdown.cn/img/favicon.ico" />
    <link rel="apple-touch-icon" href="http://www.shutdown.cn/img/apple-touch-icon.png" />
    <link rel="apple-touch-icon-precomposed" href="http://www.shutdown.cn/img/apple-touch-icon.png" />
    <link href="http://www.shutdown.cn/js/vendor/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />
    <link href="http://www.shutdown.cn/js/vendor/fancybox/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />
    <link href="http://www.shutdown.cn/css/main.css" rel="stylesheet" type="text/css" />
    <link href="http://www.shutdown.cn/css/syntax.css" rel="stylesheet" type="text/css" />
    <script type="text/javascript" id="hexo.configuration">
  var NexT = window.NexT || {};
  var CONFIG = {
    scheme: 'Pisces',
    sidebar: {"position":"left","display":"post"},
     fancybox: true, 
    motion: true
  };
</script>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-7826003325059020" crossorigin="anonymous"></script>
</head>
<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
<div class="container one-collumn sidebar-position-left page-home  ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"> <div class="site-meta  custom-logo ">

  <div class="custom-logo-site-title">
    <a href="http://www.shutdown.cn"  class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <span class="site-title">天地维杰网</span>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>
  <p class="site-subtitle">人如秋鸿来有信，事若春梦了无痕</p>
</div>

<div class="site-nav-toggle">
  <button>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
  </button>
</div>

<nav class="site-nav">
    <ul id="menu" class="menu">
      
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />首页
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/redis/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-battery-full"></i> <br />Redis
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/java/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-coffee"></i> <br />java
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/linux/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-linux"></i> <br />linux
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/daily/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-bug"></i> <br />日常问题
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/spring/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-child"></i> <br />Spring和Springboot
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/mac/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-fire"></i> <br />Mac相关
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/middleware/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-gavel"></i> <br />中间件
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/jiagou/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-rocket"></i> <br />架构
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/python/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-ship"></i> <br />python
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/front/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-bolt"></i> <br />前端
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/jvm/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-balance-scale"></i> <br />jvm
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/c/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-battery-empty"></i> <br />c语言
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/categories/web3/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-web3"></i> <br />web3
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/post/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />归档
          </a>
        </li>
      
        <li class="menu-item ">
          <a href="http://www.shutdown.cn/about/" rel="section">
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />关于
          </a>
        </li>
      
      <li class="menu-item menu-item-search">
        <a href="javascript:;" class="popup-trigger"> <i class="menu-item-icon fa fa-search fa-fw"></i> <br /> 搜索</a>
      </li>
    </ul>
    <div class="site-search">
      <div class="popup">
 <span class="search-icon fa fa-search"></span>
 <input type="text" id="local-search-input">
 <div id="local-search-result"></div>
 <span class="popup-btn-close">close</span>
</div>

    </div>
</nav>

 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            
<section id="posts" class="posts-expand">
  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
    <header class="post-header">
      <h1 class="post-title" itemprop="name headline">
        <a class="post-title-link" href="http://www.shutdown.cn/post/redis%E5%92%8Cio%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8/" itemprop="url">
        
        </a>
      </h1>
      <div class="post-meta">
      <span class="post-time">
<span class="post-meta-item-icon">
    <i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">时间：</span>
<time itemprop="dateCreated" datetime="2016-03-22T13:04:35+08:00" content="0001-01-01">
    0001-01-01
</time>
</span> 
      
      
       <span>
&nbsp; | &nbsp;
<span class="post-meta-item-icon">
    <i class="fa fa-eye"></i>
</span>
<span class="post-meta-item-text">阅读：</span>
<span class="leancloud-visitors-count">828 字 ~4分钟</span>
</span>
      </div>
    </header>
    <div class="post-body" itemprop="articleBody">
    

    

<blockquote>
<p>原文地址：<a href="https://draveness.me/redis-io-multiplexing/">https://draveness.me/redis-io-multiplexing/</a></p>
</blockquote>

<h1 id="redis-和-i-o-多路复用">Redis 和 I/O 多路复用</h1>

<p>2016-11-26 <a href="https://draveness.me/tags/redis">Redis</a> <a href="https://draveness.me/tags/数据库">数据库</a> <a href="https://draveness.me/tags/nosql">NoSQL</a></p>

<p>最近在看 UNIX 网络编程并研究了一下 Redis 的实现，感觉 Redis 的源代码十分适合阅读和分析，其中 I/O 多路复用（mutiplexing）部分的实现非常干净和优雅，在这里想对这部分的内容进行简单的整理。</p>

<h2 id="几种-i-o-模型">几种 I/O 模型</h2>

<p>为什么 Redis 中要使用 I/O 多路复用这种技术呢？</p>

<p>首先，Redis 是跑在单线程中的，所有的操作都是按照顺序线性执行的，但是由于读写操作等待用户输入或输出都是阻塞的，所以 I/O 操作在一般情况下往往不能直接返回，这会导致某一文件的 I/O 阻塞导致整个进程无法对其它客户提供服务，而 <strong>I/O 多路复用</strong>就是为了解决这个问题而出现的。</p>

<h3 id="blocking-i-o">Blocking I/O</h3>

<p>先来看一下传统的阻塞 I/O 模型到底是如何工作的：当使用 <code>read</code> 或者 <code>write</code> 对某一个<strong>文件描述符（File Descriptor 以下简称 FD)</strong>进行读写时，如果当前 FD 不可读或不可写，整个 Redis 服务就不会对其它的操作作出响应，导致整个服务不可用。</p>

<p>这也就是传统意义上的，也就是我们在编程中使用最多的阻塞模型：</p>

<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20220701113119.png" width = 900 /> </div>

<p>阻塞模型虽然开发中非常常见也非常易于理解，但是由于它会影响其他 FD 对应的服务，所以在需要处理多个客户端任务的时候，往往都不会使用阻塞模型。</p>

<h3 id="i-o-多路复用">I/O 多路复用</h3>

<blockquote>
<p>虽然还有很多其它的 I/O 模型，但是在这里都不会具体介绍。</p>
</blockquote>

<p>阻塞式的 I/O 模型并不能满足这里的需求，我们需要一种效率更高的 I/O 模型来支撑 Redis 的多个客户（redis-cli），这里涉及的就是 I/O 多路复用模型了：</p>

<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20220701113141.png" width = 900 /> </div>

<p>在 I/O 多路复用模型中，最重要的函数调用就是 <code>select</code>，该方法的能够同时监控多个文件描述符的可读可写情况，当其中的某些文件描述符可读或者可写时，<code>select</code> 方法就会返回可读以及可写的文件描述符个数。</p>

<blockquote>
<p>关于 <code>select</code> 的具体使用方法，在网络上资料很多，这里就不过多展开介绍了；</p>

<p>与此同时也有其它的 I/O 多路复用函数 <code>epoll/kqueue/evport</code>，它们相比 <code>select</code> 性能更优秀，同时也能支撑更多的服务。</p>
</blockquote>

<h2 id="reactor-设计模式">Reactor 设计模式</h2>

<p>Redis 服务采用 Reactor 的方式来实现文件事件处理器（每一个网络连接其实都对应一个文件描述符）</p>

<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20220701113159.png" width = 900 /> </div>

<p>文件事件处理器使用 I/O 多路复用模块同时监听多个 FD，当 <code>accept</code>、<code>read</code>、<code>write</code> 和 <code>close</code> 文件事件产生时，文件事件处理器就会回调 FD 绑定的事件处理器。</p>

<p>虽然整个文件事件处理器是在单线程上运行的，但是通过 I/O 多路复用模块的引入，实现了同时对多个 FD 读写的监控，提高了网络通信模型的性能，同时也可以保证整个 Redis 服务实现的简单。</p>

<h2 id="i-o-多路复用模块">I/O 多路复用模块</h2>

<p>I/O 多路复用模块封装了底层的 <code>select</code>、<code>epoll</code>、<code>avport</code> 以及 <code>kqueue</code> 这些 I/O 多路复用函数，为上层提供了相同的接口。</p>

<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20220701113225.png" width = 900 /> </div>

<p>在这里我们简单介绍 Redis 是如何包装 <code>select</code> 和 <code>epoll</code> 的，简要了解该模块的功能，整个 I/O 多路复用模块抹平了不同平台上 I/O 多路复用函数的差异性，提供了相同的接口：</p>

<ul>
<li><code>static int aeApiCreate(aeEventLoop *eventLoop)</code></li>
<li><code>static int aeApiResize(aeEventLoop *eventLoop, int setsize)</code></li>
<li><code>static void aeApiFree(aeEventLoop *eventLoop)</code></li>
<li><code>static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask)</code></li>
<li><code>static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask)</code></li>
<li><code>static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp)</code></li>
</ul>

<p>同时，因为各个函数所需要的参数不同，我们在每一个子模块内部通过一个 <code>aeApiState</code> 来存储需要的上下文信息：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#8f5902;font-style:italic">// select
</span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">typedef</span> <span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">aeApiState</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">fd_set</span> <span style="color:#000">rfds</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">wfds</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">fd_set</span> <span style="color:#000">_rfds</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">_wfds</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span> <span style="color:#000">aeApiState</span><span style="color:#000;font-weight:bold">;</span>

<span style="color:#8f5902;font-style:italic">// epoll
</span><span style="color:#8f5902;font-style:italic"></span><span style="color:#204a87;font-weight:bold">typedef</span> <span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">aeApiState</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">epfd</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">epoll_event</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">events</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span> <span style="color:#000">aeApiState</span><span style="color:#000;font-weight:bold">;</span></code></pre></div>
<p>这些上下文信息会存储在 <code>eventLoop</code> 的 <code>void *state</code> 中，不会暴露到上层，只在当前子模块中使用。</p>

<h3 id="封装-select-函数">封装 select 函数</h3>

<blockquote>
<p><code>select</code> 可以监控 FD 的可读、可写以及出现错误的情况。</p>
</blockquote>

<p>在介绍 I/O 多路复用模块如何对 <code>select</code> 函数封装之前，先来看一下 <code>select</code> 函数使用的大致流程：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">fd</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#8f5902;font-style:italic">/* file descriptor */</span>

<span style="color:#000">fd_set</span> <span style="color:#000">rfds</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000">FD_ZERO</span><span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">rfds</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000">FD_SET</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">fd</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">rfds</span><span style="color:#000;font-weight:bold">)</span>

<span style="color:#204a87;font-weight:bold">for</span> <span style="color:#000;font-weight:bold">(</span> <span style="color:#000;font-weight:bold">;</span> <span style="color:#000;font-weight:bold">;</span> <span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">select</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">fd</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">rfds</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87">NULL</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87">NULL</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87">NULL</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">FD_ISSET</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">fd</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">rfds</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#8f5902;font-style:italic">/* file descriptor `fd` becomes readable */</span>
    <span style="color:#000;font-weight:bold">}</span>
<span style="color:#000;font-weight:bold">}</span></code></pre></div>
<ol>
<li>初始化一个可读的 <code>fd_set</code> 集合，保存需要监控可读性的 FD；</li>
<li>使用 <code>FD_SET</code> 将 <code>fd</code> 加入 <code>rfds</code>；</li>
<li>调用 <code>select</code> 方法监控 <code>rfds</code> 中的 FD 是否可读；</li>
<li>当 <code>select</code> 返回时，检查 FD 的状态并完成对应的操作。</li>
</ol>

<p>而在 Redis 的 <code>ae_select</code> 文件中代码的组织顺序也是差不多的，首先在 <code>aeApiCreate</code> 函数中初始化 <code>rfds</code> 和 <code>wfds</code>：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#204a87;font-weight:bold">static</span> <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">aeApiCreate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">aeEventLoop</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">eventLoop</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">aeApiState</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">state</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">zmalloc</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">sizeof</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">aeApiState</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">state</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">FD_ZERO</span><span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">rfds</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">FD_ZERO</span><span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">wfds</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">apidata</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">state</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span></code></pre></div>
<p>而 <code>aeApiAddEvent</code> 和 <code>aeApiDelEvent</code> 会通过 <code>FD_SET</code> 和 <code>FD_CLR</code> 修改 <code>fd_set</code> 中对应 FD 的标志位：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#204a87;font-weight:bold">static</span> <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">aeApiAddEvent</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">aeEventLoop</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">eventLoop</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">fd</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">mask</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">aeApiState</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">state</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">apidata</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">AE_READABLE</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">FD_SET</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">fd</span><span style="color:#000;font-weight:bold">,</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">rfds</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">AE_WRITABLE</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">FD_SET</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">fd</span><span style="color:#000;font-weight:bold">,</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">wfds</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span></code></pre></div>
<p>整个 <code>ae_select</code> 子模块中最重要的函数就是 <code>aeApiPoll</code>，它是实际调用 <code>select</code> 函数的部分，其作用就是在 I/O 多路复用函数返回时，将对应的 FD 加入 <code>aeEventLoop</code> 的 <code>fired</code> 数组中，并返回事件的个数：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#204a87;font-weight:bold">static</span> <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">aeApiPoll</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">aeEventLoop</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">eventLoop</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">timeval</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">tvp</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">aeApiState</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">state</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">apidata</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">retval</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">j</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">numevents</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#000">memcpy</span><span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">_rfds</span><span style="color:#000;font-weight:bold">,</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">rfds</span><span style="color:#000;font-weight:bold">,</span><span style="color:#204a87;font-weight:bold">sizeof</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">fd_set</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">memcpy</span><span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">_wfds</span><span style="color:#000;font-weight:bold">,</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">wfds</span><span style="color:#000;font-weight:bold">,</span><span style="color:#204a87;font-weight:bold">sizeof</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">fd_set</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#000">retval</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">select</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">maxfd</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">,</span>
                <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">_rfds</span><span style="color:#000;font-weight:bold">,</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">_wfds</span><span style="color:#000;font-weight:bold">,</span><span style="color:#204a87">NULL</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">tvp</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">retval</span> <span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#204a87;font-weight:bold">for</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">j</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#000">j</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">maxfd</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#000">j</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
            <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">aeFileEvent</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">fe</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">events</span><span style="color:#000;font-weight:bold">[</span><span style="color:#000">j</span><span style="color:#000;font-weight:bold">]</span><span style="color:#000;font-weight:bold">;</span>

            <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">fe</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">AE_NONE</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#204a87;font-weight:bold">continue</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">fe</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">AE_READABLE</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">FD_ISSET</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">j</span><span style="color:#000;font-weight:bold">,</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">_rfds</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span>
                <span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">|</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">AE_READABLE</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">fe</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">AE_WRITABLE</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">FD_ISSET</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">j</span><span style="color:#000;font-weight:bold">,</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">_wfds</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span>
                <span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">|</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">AE_WRITABLE</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">fired</span><span style="color:#000;font-weight:bold">[</span><span style="color:#000">numevents</span><span style="color:#000;font-weight:bold">]</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fd</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">j</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">fired</span><span style="color:#000;font-weight:bold">[</span><span style="color:#000">numevents</span><span style="color:#000;font-weight:bold">]</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">mask</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">numevents</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000;font-weight:bold">}</span>
    <span style="color:#000;font-weight:bold">}</span>
    <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">numevents</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span></code></pre></div>
<h3 id="封装-epoll-函数">封装 epoll 函数</h3>

<p>Redis 对 <code>epoll</code> 的封装其实也是类似的，使用 <code>epoll_create</code> 创建 <code>epoll</code> 中使用的 <code>epfd</code>：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#204a87;font-weight:bold">static</span> <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">aeApiCreate</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">aeEventLoop</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">eventLoop</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">aeApiState</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">state</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">zmalloc</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">sizeof</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">aeApiState</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">state</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">events</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">zmalloc</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">sizeof</span><span style="color:#000;font-weight:bold">(</span><span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">epoll_event</span><span style="color:#000;font-weight:bold">)</span><span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">setsize</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#ce5c00;font-weight:bold">!</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">events</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#000">zfree</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">state</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000;font-weight:bold">}</span>
    <span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">epfd</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">epoll_create</span><span style="color:#000;font-weight:bold">(</span><span style="color:#0000cf;font-weight:bold">1024</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#8f5902;font-style:italic">/* 1024 is just a hint for the kernel */</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">epfd</span> <span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#000">zfree</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">events</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000">zfree</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">state</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000;font-weight:bold">}</span>
    <span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">apidata</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">state</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span></code></pre></div>
<p>在 <code>aeApiAddEvent</code> 中使用 <code>epoll_ctl</code> 向 <code>epfd</code> 中添加需要监控的 FD 以及监听的事件：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#204a87;font-weight:bold">static</span> <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">aeApiAddEvent</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">aeEventLoop</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">eventLoop</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">fd</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">mask</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">aeApiState</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">state</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">apidata</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">epoll_event</span> <span style="color:#000">ee</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000;font-weight:bold">{</span><span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">}</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#8f5902;font-style:italic">/* avoid valgrind warning */</span>
    <span style="color:#8f5902;font-style:italic">/* If the fd was already monitored for some event, we need a MOD
</span><span style="color:#8f5902;font-style:italic">     * operation. Otherwise we need an ADD operation. */</span>
    <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">op</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">events</span><span style="color:#000;font-weight:bold">[</span><span style="color:#000">fd</span><span style="color:#000;font-weight:bold">]</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">AE_NONE</span> <span style="color:#ce5c00;font-weight:bold">?</span>
            <span style="color:#f57900">EPOLL_CTL_ADD</span> <span style="color:#000;font-weight:bold">:</span> <span style="color:#000">EPOLL_CTL_MOD</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#000">ee</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">events</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">|</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">events</span><span style="color:#000;font-weight:bold">[</span><span style="color:#000">fd</span><span style="color:#000;font-weight:bold">]</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">mask</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#8f5902;font-style:italic">/* Merge old events */</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">AE_READABLE</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">ee</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">events</span> <span style="color:#ce5c00;font-weight:bold">|</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">EPOLLIN</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">AE_WRITABLE</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">ee</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">events</span> <span style="color:#ce5c00;font-weight:bold">|</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">EPOLLOUT</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">ee</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">data</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fd</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">fd</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">epoll_ctl</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">epfd</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">op</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">fd</span><span style="color:#000;font-weight:bold">,</span><span style="color:#ce5c00;font-weight:bold">&amp;</span><span style="color:#000">ee</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span></code></pre></div>
<p>C</p>

<p>由于 <code>epoll</code> 相比 <code>select</code> 机制略有不同，在 <code>epoll_wait</code> 函数返回时并不需要遍历所有的 FD 查看读写情况；在 <code>epoll_wait</code> 函数返回时会提供一个 <code>epoll_event</code> 数组：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#204a87;font-weight:bold">typedef</span> <span style="color:#204a87;font-weight:bold">union</span> <span style="color:#000">epoll_data</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#204a87;font-weight:bold">void</span>    <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">ptr</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">int</span>      <span style="color:#000">fd</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#8f5902;font-style:italic">/* 文件描述符 */</span>
    <span style="color:#000">uint32_t</span> <span style="color:#000">u32</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#000">uint64_t</span> <span style="color:#000">u64</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span> <span style="color:#000">epoll_data_t</span><span style="color:#000;font-weight:bold">;</span>

<span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">epoll_event</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">uint32_t</span>     <span style="color:#000">events</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#8f5902;font-style:italic">/* Epoll 事件 */</span>
    <span style="color:#000">epoll_data_t</span> <span style="color:#000">data</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span><span style="color:#000;font-weight:bold">;</span></code></pre></div>
<blockquote>
<p>其中保存了发生的 <code>epoll</code> 事件（<code>EPOLLIN</code>、<code>EPOLLOUT</code>、<code>EPOLLERR</code> 和 <code>EPOLLHUP</code>）以及发生该事件的 FD。</p>
</blockquote>

<p><code>aeApiPoll</code> 函数只需要将 <code>epoll_event</code> 数组中存储的信息加入 <code>eventLoop</code> 的 <code>fired</code> 数组中，将信息传递给上层模块：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#204a87;font-weight:bold">static</span> <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">aeApiPoll</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">aeEventLoop</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">eventLoop</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">timeval</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">tvp</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
    <span style="color:#000">aeApiState</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">state</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">apidata</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">retval</span><span style="color:#000;font-weight:bold">,</span> <span style="color:#000">numevents</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>

    <span style="color:#000">retval</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">epoll_wait</span><span style="color:#000;font-weight:bold">(</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">epfd</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">events</span><span style="color:#000;font-weight:bold">,</span><span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">setsize</span><span style="color:#000;font-weight:bold">,</span>
            <span style="color:#000">tvp</span> <span style="color:#ce5c00;font-weight:bold">?</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">tvp</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">tv_sec</span><span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#0000cf;font-weight:bold">1000</span> <span style="color:#ce5c00;font-weight:bold">+</span> <span style="color:#000">tvp</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">tv_usec</span><span style="color:#ce5c00;font-weight:bold">/</span><span style="color:#0000cf;font-weight:bold">1000</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#ce5c00;font-weight:bold">:</span> <span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#0000cf;font-weight:bold">1</span><span style="color:#000;font-weight:bold">)</span><span style="color:#000;font-weight:bold">;</span>
    <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">retval</span> <span style="color:#ce5c00;font-weight:bold">&gt;</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
        <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">j</span><span style="color:#000;font-weight:bold">;</span>

        <span style="color:#000">numevents</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">retval</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#204a87;font-weight:bold">for</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">j</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#000">j</span> <span style="color:#ce5c00;font-weight:bold">&lt;</span> <span style="color:#000">numevents</span><span style="color:#000;font-weight:bold">;</span> <span style="color:#000">j</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000;font-weight:bold">{</span>
            <span style="color:#204a87;font-weight:bold">int</span> <span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#0000cf;font-weight:bold">0</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#204a87;font-weight:bold">struct</span> <span style="color:#000">epoll_event</span> <span style="color:#ce5c00;font-weight:bold">*</span><span style="color:#000">e</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">state</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">events</span><span style="color:#ce5c00;font-weight:bold">+</span><span style="color:#000">j</span><span style="color:#000;font-weight:bold">;</span>

            <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">e</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">events</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">EPOLLIN</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">|</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">AE_READABLE</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">e</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">events</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">EPOLLOUT</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">|</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">AE_WRITABLE</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">e</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">events</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">EPOLLERR</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">|</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">AE_WRITABLE</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#204a87;font-weight:bold">if</span> <span style="color:#000;font-weight:bold">(</span><span style="color:#000">e</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">events</span> <span style="color:#ce5c00;font-weight:bold">&amp;</span> <span style="color:#000">EPOLLHUP</span><span style="color:#000;font-weight:bold">)</span> <span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">|</span><span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">AE_WRITABLE</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">fired</span><span style="color:#000;font-weight:bold">[</span><span style="color:#000">j</span><span style="color:#000;font-weight:bold">]</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fd</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">e</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">data</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">fd</span><span style="color:#000;font-weight:bold">;</span>
            <span style="color:#000">eventLoop</span><span style="color:#ce5c00;font-weight:bold">-</span><span style="color:#ce5c00;font-weight:bold">&gt;</span><span style="color:#000">fired</span><span style="color:#000;font-weight:bold">[</span><span style="color:#000">j</span><span style="color:#000;font-weight:bold">]</span><span style="color:#000;font-weight:bold">.</span><span style="color:#000">mask</span> <span style="color:#ce5c00;font-weight:bold">=</span> <span style="color:#000">mask</span><span style="color:#000;font-weight:bold">;</span>
        <span style="color:#000;font-weight:bold">}</span>
    <span style="color:#000;font-weight:bold">}</span>
    <span style="color:#204a87;font-weight:bold">return</span> <span style="color:#000">numevents</span><span style="color:#000;font-weight:bold">;</span>
<span style="color:#000;font-weight:bold">}</span></code></pre></div>
<h3 id="子模块的选择">子模块的选择</h3>

<p>因为 Redis 需要在多个平台上运行，同时为了最大化执行的效率与性能，所以会根据编译平台的不同选择不同的 I/O 多路复用函数作为子模块，提供给上层统一的接口；在 Redis 中，我们通过宏定义的使用，合理的选择不同的子模块：</p>
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#8f5902;font-style:italic">#</span><span style="color:#8f5902;font-style:italic">ifdef HAVE_EVPORT</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span><span style="color:#8f5902;font-style:italic">#</span><span style="color:#8f5902;font-style:italic">include</span> <span style="color:#8f5902;font-style:italic">&#34;ae_evport.c&#34;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span><span style="color:#8f5902;font-style:italic">#</span><span style="color:#8f5902;font-style:italic">else</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#8f5902;font-style:italic">#</span><span style="color:#8f5902;font-style:italic">ifdef HAVE_EPOLL</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#8f5902;font-style:italic">#</span><span style="color:#8f5902;font-style:italic">include</span> <span style="color:#8f5902;font-style:italic">&#34;ae_epoll.c&#34;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#8f5902;font-style:italic">#</span><span style="color:#8f5902;font-style:italic">else</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#8f5902;font-style:italic">#</span><span style="color:#8f5902;font-style:italic">ifdef HAVE_KQUEUE</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#8f5902;font-style:italic">#</span><span style="color:#8f5902;font-style:italic">include</span> <span style="color:#8f5902;font-style:italic">&#34;ae_kqueue.c&#34;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#8f5902;font-style:italic">#</span><span style="color:#8f5902;font-style:italic">else</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#8f5902;font-style:italic">#</span><span style="color:#8f5902;font-style:italic">include</span> <span style="color:#8f5902;font-style:italic">&#34;ae_select.c&#34;</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span>        <span style="color:#8f5902;font-style:italic">#</span><span style="color:#8f5902;font-style:italic">endif</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span>    <span style="color:#8f5902;font-style:italic">#</span><span style="color:#8f5902;font-style:italic">endif</span><span style="color:#8f5902;font-style:italic">
</span><span style="color:#8f5902;font-style:italic"></span><span style="color:#8f5902;font-style:italic">#</span><span style="color:#8f5902;font-style:italic">endif</span></code></pre></div>
<p>因为 <code>select</code> 函数是作为 POSIX 标准中的系统调用，在不同版本的操作系统上都会实现，所以将其作为保底方案：</p>

<div  align="center"><img src="https://wejack639.oss-cn-beijing.aliyuncs.com/blogimages/img/20220701113409.png" width = 900 /> </div>

<p>Redis 会优先选择时间复杂度为 𝑂(1)O(1) 的 I/O 多路复用函数作为底层实现，包括 Solaries 10 中的 <code>evport</code>、Linux 中的 <code>epoll</code> 和 macOS/FreeBSD 中的 <code>kqueue</code>，上述的这些函数都使用了内核内部的结构，并且能够服务几十万的文件描述符。</p>

<p>但是如果当前编译环境没有上述函数，就会选择 <code>select</code> 作为备选方案，由于其在使用时会扫描全部监听的描述符，所以其时间复杂度较差 𝑂(𝑛)O(n)，并且只能同时服务 1024 个文件描述符，所以一般并不会以 <code>select</code> 作为第一方案使用。</p>

<h2 id="总结">总结</h2>

<p>Redis 对于 I/O 多路复用模块的设计非常简洁，通过宏保证了 I/O 多路复用模块在不同平台上都有着优异的性能，将不同的 I/O 多路复用函数封装成相同的 API 提供给上层使用。</p>

<p>整个模块使 Redis 能以单进程运行的同时服务成千上万个文件描述符，避免了由于多进程应用的引入导致代码实现复杂度的提升，减少了出错的可能性。</p>

<h2 id="reference">Reference</h2>

<ul>
<li><a href="http://man7.org/linux/man-pages/man2/select.2.html">Select-Man-Pages</a></li>
<li><a href="https://en.wikipedia.org/wiki/Reactor_pattern">Reactor-Pattern</a></li>
<li>[epoll vs kqueue](</li>
</ul>

    </div>
    <footer class="post-footer">
     

     <div class="post-nav">
    <div class="post-nav-next post-nav-item">
    
        <a href="http://www.shutdown.cn/post/redis%E5%91%BD%E4%BB%A4%E5%9B%9E%E6%BA%AF%E6%96%B9%E6%A1%88/" rel="next" title="">
        <i class="fa fa-chevron-left"></i> 
        </a>
    
    </div>

    <div class="post-nav-prev post-nav-item">
    
        <a href="http://www.shutdown.cn/post/redis%E5%94%AF%E5%BF%AB%E4%B8%8D%E7%A0%B4%E7%9A%84%E7%A7%98%E5%AF%86/" rel="prev" title="">
         <i class="fa fa-chevron-right"></i>
        </a>
    
    </div>
</div>
      
     
     
     






    </footer>
  </article>
</section>

          </div>
        </div>
        <div class="sidebar-toggle">
  <div class="sidebar-toggle-line-wrap">
    <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
    <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
    <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
  </div>
</div>
<aside id="sidebar" class="sidebar">
  <div class="sidebar-inner">

    <section class="site-overview sidebar-panel  sidebar-panel-active ">
      <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image"
        src="http://www.shutdown.cn/img/author.jpg"
        alt="不与天斗Domino" />
    <p class="site-author-name" itemprop="name">不与天斗Domino</p>
    <p class="site-description motion-element" itemprop="description"> 
        Programmer &amp; Architect</p>
</div>
      <nav class="site-state motion-element">
    <div class="site-state-item site-state-posts">
      <a href="http://www.shutdown.cn/post/">
        <span class="site-state-item-count">183</span>
        <span class="site-state-item-name">日志</span>
      </a>
    </div>
    <div class="site-state-item site-state-categories">    
        <a href="http://www.shutdown.cn/categories/">      
         
        <span class="site-state-item-count">15</span>
        
        <span class="site-state-item-name">分类</span>
        
        </a>
    </div>

    <div class="site-state-item site-state-tags">
        <a href="http://www.shutdown.cn/tags/">
         
        <span class="site-state-item-count">224</span>
        
        <span class="site-state-item-name">标签</span>
        </a>
    </div>
</nav>
      
      

      

      <div class="links-of-blogroll motion-element inline">
<script type="text/javascript" src="//rf.revolvermaps.com/0/0/8.js?i=&amp;m=0&amp;s=220&amp;c=ff0000&amp;cr1=ffffff&amp;f=arial&amp;l=33&amp;bv=35" async="async"></script>
</div>

    </section>
    
  </div>
</aside>

      </div>
    </main>
   
    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright" >
  <span itemprop="copyrightYear">  &copy; 
  2013 - 2023</span>
  <span class="with-love"><i class="fa fa-heart"></i></span>
  <span class="author" itemprop="copyrightHolder">天地维杰网</span>
  <span class="icp" itemprop="copyrightHolder"><a href="https://beian.miit.gov.cn/" target="_blank">京ICP备13019191号-1</a></span>
</div>
<div class="powered-by">
  Powered by - <a class="theme-link" href="http://gohugo.io" target="_blank" title="hugo" >Hugo v0.63.2</a>
</div>
<div class="theme-info">
  Theme by - <a class="theme-link" href="https://github.com/xtfly/hugo-theme-next" target="_blank"> NexT
  </a>
</div>


      </div>
    </footer>

    <div class="back-to-top">
      <i class="fa fa-arrow-up"></i>
      <span id="scrollpercent"><span>0</span>%</span>
    </div>
  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/jquery/index.js?v=2.1.3"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/fastclick/lib/fastclick.min.js?v=1.0.6"></script> 
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/velocity/velocity.min.js?v=1.2.1"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/vendor/velocity/velocity.ui.min.js?v=1.2.1"></script>
<script src="http://www.shutdown.cn/js/vendor/ua-parser-js/dist/ua-parser.min.js?v=0.7.9"></script>

<script src="http://www.shutdown.cn/js/vendor/fancybox/jquery.fancybox.pack.js?v=2.1.5"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/utils.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/motion.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/affix.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/schemes/pisces.js"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/scrollspy.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/post-details.js"></script>
<script type="text/javascript" src="http://www.shutdown.cn/js/toc.js"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/bootstrap.js"></script>

<script type="text/javascript" src="http://www.shutdown.cn/js/search.js"></script>
<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
    extensions: ["tex2jax.js"],
    jax: ["input/TeX", "output/HTML-CSS"],
    tex2jax: {
      inlineMath: [ ['$','$'] ],
      displayMath: [ ['$$','$$'] ],
      processEscapes: true
    },
    "HTML-CSS": { fonts: ["TeX"] }
  });
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS-MML_HTMLorMML' async></script>
</body>
</html>